#include "Lighting.h"

#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define POS(a) MAX(a, 0)

using namespace CG;

void BlinnPhong::calculate(Mesh* mesh, Lighting* light, point3f viewer)
{
	// 修改容器大小
	mesh->m_cList.resize(mesh->VertexNum(), { 0, 0, 0 });

	// 计算光照
	for (int i = 0; i < mesh->VertexNum(); i++)
	{
		vertex3f* nv = mesh->m_vList[i];
		point3f np = nv->pos;

		// 三个关键方向 l n v，n
		vector3f n = mesh->m_nList[i];
		vector3f l;
		vector3f v;

		// 初始化光照
		double alpha = mesh->m_material.alpha;
		double d = 0;

		// 判断光源是否是平行光源
		if (light->l.w == 1)
		{
			l = normalize(point3f(light->l.x, light->l.y, light->l.z) - np);
			v = normalize(viewer - np);

			// 计算光源距离，a+bd+cd^2 得到衰减因子
			d = distance(np, point3f(light->l.x, light->l.y, light->l.z));
			d = 1.0 / (light->a + light->b * d + light->c * d * d);

			// 再计算角度衰减，如果夹角是负数，那么直接衰减为零
			// 注意我们之前反转了 l，所以点积后要取相反数
			double cos_alpha = -dot(light->center, l);

			// 夹角超过圆锥角，则归零
			if (cos_alpha < cos(light->theta))
				cos_alpha = 0;

			d *= pow(cos_alpha, light->al);
		}
		else
		{
			l = normalize(vector3f(-light->l.x, -light->l.y, -light->l.z));
			v = normalize(viewer - np);

			// 平行光源不衰减
			d = 1;
		}

		// 计算半角向量
		vector3f h = normalize(l + v);

		// 根据夹角计算衰减系数：注意先对夹角余弦取正，然后再作用指数 alpha
		double l_dot_n = POS(dot(l, n));
		double n_dot_h = pow(POS(dot(n, h)), alpha);

		// 顺序 a,d,s
		rgb_space I;
		I.r = mesh->m_material.R[0][0] * light->L[0][0] +
			(mesh->m_material.R[1][0] * light->L[1][0] * l_dot_n +
				mesh->m_material.R[2][0] * light->L[2][0] * n_dot_h) *
			d;
		I.g = mesh->m_material.R[0][1] * light->L[0][1] +
			(mesh->m_material.R[1][1] * light->L[1][1] * l_dot_n +
				mesh->m_material.R[2][1] * light->L[2][1] * n_dot_h) *
			d;
		I.b = mesh->m_material.R[0][2] * light->L[0][2] +
			(mesh->m_material.R[1][2] * light->L[1][2] * l_dot_n +
				mesh->m_material.R[2][2] * light->L[2][2] * n_dot_h) *
			d;

		// 防止颜色越界
		I.r = MIN(MAX(I.r, 0), 1);
		I.g = MIN(MAX(I.g, 0), 1);
		I.b = MIN(MAX(I.b, 0), 1);

		// 附加颜色
		mesh->m_cList[i] = mesh->m_cList[i] + I;
	}
}